﻿/*
 * Author: Duncan Jenkins
 *  
 * Description: Clusters all the colors in an image into K seperate clusters (K-Means)
 * */

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;

namespace LibraryCS {
    public class KMeans : VisionAlgorithm {
        private int Means;
        public List<Mean> means = new List<Mean>();

        public KMeans(String name) : base(name) { }

        public override Bitmap DoAlgorithm(Bitmap sourceImage) {
            if (means.Count != 0) {
                means.Clear();
            }
            VImage ops = new VImage(sourceImage);
            VImage destOps = new VImage(sourceImage.Width, sourceImage.Height);
            Random random = new Random();
            bool clustering = true;

            Debug.WriteLine("Creating " + Means + " means");
            for (int i = 0; i < Means; i++) {
                means.Add(new Mean(random.Next(255), random.Next(255), random.Next(255), i));
            }

            Debug.WriteLine("Clustering commencing");
            while (clustering) {
                Debug.WriteLine("Assigning Pixels to Means");
                for (int i = 0; i < ops.RedPixels.Length; i++) {
                    int red = ops.RedPixels[i];
                    int green = ops.GreenPixels[i];
                    int blue = ops.BluePixels[i];

                    Mean closestMean = null;
                    double distance = 10000;
                    foreach (Mean mean in means) {
                        // Calculate closest mean
                        double currentDistance = Math.Sqrt((Math.Pow((blue - mean.Blue), 2.0)) + (Math.Pow((red - mean.Red), 2.0)) + (Math.Pow((green - mean.Green), 2.0)));
                        //Debug.WriteLine("Distance: " + currentDistance);
                        if (currentDistance < distance) {
                            distance = currentDistance;
                            closestMean = mean;
                        }
                    }

                    // Assign pixel to mean
                    closestMean.addPixel(i, red, green, blue);

                    clustering = false;
                }

                Debug.WriteLine("Calculating new mean positions");
                foreach (Mean mean in means) {
                    bool stillMoving = mean.calculateMean();
                    if (stillMoving) {
                        clustering = true;
                        mean.pixels.Clear();
                    }
                }
            }

            Debug.WriteLine("Assigning pixel colors");
            foreach (Mean mean in means) {
                for (int i = 0; i < mean.pixels.Count; i++) {
                    destOps.RedPixels[mean.pixels[i]] = (byte)mean.Red;
                    destOps.GreenPixels[mean.pixels[i]] = (byte)mean.Green;
                    destOps.BluePixels[mean.pixels[i]] = (byte)mean.Blue;
                }
            }
            destOps.ApplyPixelChanges();

            return destOps.localImage;
        }

        #region GUI Functions

        public override List<System.Windows.Forms.Control> createControls() {
            List<Control> Controls = new List<Control>();

            // Create a Label indictating the type of setting this is
            Label lb = new Label();
            lb.AutoSize = true;
            lb.Text = "Means";
            lb.Name = "lbMeans";

            // Create a TextBox Control for setting the number of means
            TextBox tb = new TextBox();
            tb.Name = "tbMeans";
            tb.AcceptsReturn = false;
            tb.MaxLength = 3;
            tb.Multiline = false;
            tb.Size = new Size(50, 25);

            // Add the controls to the list
            Controls.Add(tb);
            Controls.Add(lb);

            // Return the list
            return Controls;
        }

        public override void setSettings(List<object> settings) {
            Means = Int32.Parse(((TextBox)settings[0]).Text);
            Debug.WriteLine("Settings entered: " + Means);
        }

        #endregion
    }
}
